package com.neo.tiny.business.queue.api.order;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.neo.tiny.business.queue.api.TicketOrderApi;
import com.neo.tiny.business.queue.api.vo.call.ListTicketForCallRes;
import com.neo.tiny.business.queue.dto.order.TicketOrderReqDTO;
import com.neo.tiny.business.queue.dto.order.TicketOrderResDTO;
import com.neo.tiny.business.queue.entity.*;
import com.neo.tiny.business.queue.enums.TicketStatusEnum;
import com.neo.tiny.business.queue.service.*;
import com.neo.tiny.common.constant.CommonConstants;
import com.neo.tiny.common.util.CommonDoTransfer;
import com.neo.tiny.query.LambdaQueryWrapperBase;
import com.neo.tiny.service.RedisService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @Description: 取号业务
 * @Author: yqz
 * @CreateDate: 2022/10/30 00:12
 */
@Slf4j
@Service
@AllArgsConstructor
public class TicketOrderApiImpl implements TicketOrderApi {


    private final TicketOrderService ticketOrderService;

    private final RedisService redisService;

    private final MachineService machineService;

    private final QueueService queueService;

    private final QueueWindowRelationService queueWindowRelationService;

    private final WindowService windowService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public TicketOrderResDTO createTicketOrder(TicketOrderReqDTO req) {

        MachineDO machine = machineService.getOne(new LambdaQueryWrapper<MachineDO>()
                .eq(MachineDO::getTicketMachineId, req.getTicketMachineId()));

        // 取号机票号
        String ticketNo = generateTicketNo(machine.getTicketMachineKey());
        log.info("客户：{}，取号票号：{}", req.getCustomerName(), ticketNo);
        QueueDO queue = queueService.getById(req.getQueueId());
        // 保存取号数据
        TicketOrderDO orderDO = new TicketOrderDO();
        orderDO.setTicketNo(ticketNo);
        orderDO.setBusinessId(req.getBusinessId());
        orderDO.setCustomerName(req.getCustomerName());
        orderDO.setCustomerIdNo(req.getCustomerIdNo());
        orderDO.setCustomerPhone(req.getCustomerPhone());
        orderDO.setQueueId(req.getQueueId());
        orderDO.setQueueKey(queue.getQueueKey());
        orderDO.setQueueName(queue.getQueueName());
        orderDO.setTicketPrintTime(LocalDateTime.now());
        orderDO.setTicketStatus(TicketStatusEnum.TICKET_NO.getStatus());
        orderDO.setTicketMachineId(req.getTicketMachineId());
        orderDO.setTicketMachineKey(machine.getTicketMachineKey());
        orderDO.setTicketMachineName(machine.getTicketMachineName());
        ticketOrderService.save(orderDO);

        return TicketOrderResDTO.builder()
                .ticketNo(ticketNo)
                .businessId(req.getBusinessId())
                .build();
    }

    @Override
    public String generateTicketNo(String ticketMachineKey) {

        LocalDateTime midnight = LocalDateTime.now().plusDays(1).withHour(0)
                .withMinute(0).withSecond(0).withNano(0);
        // 计算今天还有多少秒，为自增取号数据设置过期时间，以防第二天未清零
        long seconds = ChronoUnit.SECONDS.between(LocalDateTime.now(), midnight);

        String key = CommonConstants.TICKET_INCR + StrUtil.COLON + ticketMachineKey;
        Long no = redisService.incr(key, 1);
        redisService.expire(key, seconds);
        String ticketNo = no.toString();
        // 如果小于10，则追加0
        if (no < 10) {
            ticketNo = "0" + no;
        }
        return ticketMachineKey + StrUtil.C_UNDERLINE + ticketNo;
    }

    @Override
    public List<ListTicketForCallRes> listTicketForCallByWindowId(Long windowId, Collection<Integer> status) {


        // 根据窗口id查询对应的队列id集合
        List<QueueWindowRelationDO> list = queueWindowRelationService.list(new LambdaQueryWrapperBase<QueueWindowRelationDO>()
                .eqIfPresent(QueueWindowRelationDO::getWindowId, windowId));
        Set<Long> queueIds = list.stream().map(QueueWindowRelationDO::getQueueId).collect(Collectors.toSet());

        if (CollUtil.isEmpty(queueIds)) {
            return new ArrayList<>();
        }
        // 根据队列id集合查询对应的取号列表
        List<TicketOrderDO> queueList = ticketOrderService.list(new LambdaQueryWrapperBase<TicketOrderDO>()
                .inIfPresent(TicketOrderDO::getTicketStatus, status)
                .inIfPresent(TicketOrderDO::getQueueId, queueIds)
                .orderByDesc(TicketOrderDO::getTicketStatus));

        return CommonDoTransfer.transfer(queueList, ListTicketForCallRes.class);
    }

    @Override
    public ListTicketForCallRes call(Long orderId, Long windowId, Integer ticketStatus) {

        // 根据取号主键获取取号数据
        TicketOrderDO order = ticketOrderService.getById(orderId);
        if (TicketStatusEnum.LEAVE.getStatus().equals(ticketStatus) || TicketStatusEnum.PASS_TICKET.getStatus().equals(ticketStatus)) {
            Assert.isTrue(order.getWindowId().equals(windowId), "非当前窗口呼叫数据，不可更改");
        }

        // 根据窗口id获取窗口信息
        WindowDO window = windowService.getById(windowId);

        ticketOrderService.update(new LambdaUpdateWrapper<TicketOrderDO>()
                .set(TicketOrderDO::getWindowId, windowId)
                .set(TicketOrderDO::getWindowNo, window.getWindowNo())
                .set(TicketOrderDO::getWindowName, window.getWindowName())
                .set(TicketOrderDO::getTicketStatus, ticketStatus)
                .set(TicketOrderDO::getTicketCallTime, LocalDateTime.now())
                .eq(TicketOrderDO::getOrderId, orderId));

        return ListTicketForCallRes.builder()
                .orderId(orderId)
                .windowId(windowId)
                .ticketCallTime(order.getTicketCallTime())
                .build();
    }
}
